#include <cstdio>
#include <cstdlib>
//#include <tchar.h>

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/video/video.hpp"
#include <opencv2/gpu/gpu.hpp>

#include "VideoRead.h"

#include "FeatureComputer.h"

#include "Classifier.h"

#include "Bagging.h"

#include "ClassifierRank.h"

#include "Interface.h"

void crossFrameTest(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						const char * feature_code_string,
						LcFeatureExtractor * my_extractor,
						LcClassifier * my_classifier);


void estimateCluster(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						const char * feature_code_string,
						LcGlobalExtractor * global_extractor,
						string addition_str,
						bool IF_KMEAN,
						bool IF_REG);

void getGlobalFeature(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						Mat & feat_train,
						Mat & feat_test,
						LcGlobalExtractor * global_extractor,
						bool reload = false);

void initCrossValidator(	LcVideoReadExt * test_reader,
							LcVideoReadExt * train_reader,
							const char * feature_code_string,
							vector<vector< LcValidator > > & train2train,
							vector<vector< LcValidator > > & test2train );

Mat getGlobalFeatAffine(Mat & global_feature);
Mat getClassifierAffine( vector<vector< LcValidator > >  & train2train);


Mat getFinOrder(	vector<vector< LcValidator > >  & train2train,
					vector< int > & order );

void clusterLab2Map(	vector< int > & cluster_lab, 
							vector< vector< int > >  & frm_id);

void EMcluster( Mat & specturm, std::vector<int> & lb,int cluster_n);

Mat getLaplacian( Mat & D);

void crankCluster(	vector<vector< LcValidator > > & train2train,
					Mat & feature_train,
					vector< int > & cluster_lab,
					float beta = 1.0f, float val = 1.0f);

void kmeanCluster(	vector<vector< LcValidator > > & train2train,
					Mat & feature_train,
					vector< int > & cluster_lab);

//========================================

class LcWeakRTReg :public LcClassifier
{
private:
	CvRTParams _params;
	CvRTrees _random_tree;
public:
	void train(Mat & feature, Mat & label);

	LcValidator predict( Mat & feature, Mat & res, Mat & label);
};

void LcWeakRTReg::train(Mat & feature, Mat & label)
{
	_params.max_depth = 3;	
	_params.min_sample_count = 3;
	_params.regression_accuracy = 0;
	_params.nactive_vars = feature.cols;

	Mat varType = Mat::ones(feature.cols+1,1,CV_8UC1) * CV_VAR_NUMERICAL; // all floats	

	_random_tree.train( feature, CV_ROW_SAMPLE,label,Mat(),Mat(),varType,Mat(), _params);
}

LcValidator LcWeakRTReg::predict( Mat & feature, Mat & res, Mat & label)
{
	int n = feature.rows;
	res = Mat::zeros( n, 1, 5);
	for(int i = 0; i< n ; i++)
	{
		res.at<float>(i,0) =  _random_tree.predict( feature.row(i) );		
	}

	if( label.rows == feature.rows ) return LcValidator( res, label);
	else return LcValidator();
}

//========================================

LcValidator predictFromOneCluster(	vector<vector< LcValidator > > & validators,
									int predict_id,
									vector< int > & frm_id );

LcValidator getClusterValidator(	vector< vector< int > > & frm_id,
									vector< int > & cluster_lab,
									Mat & retrival_feature,
									Mat & test_feature,
									vector<vector< LcValidator > > & scores
									);

LcValidator getRegClusterValidator(	vector< vector< int > > & frm_id,
									vector< int > & cluster_lab,
									Mat & retrival_feature,
									Mat & test_feature,
									vector<vector< LcValidator > > & scores,
									vector< LcWeakRTReg > & regressions
									);

void regressionScore(	vector<vector< LcValidator > > & train2train, 
						vector< vector< int > > & frm_id,
						Mat & feat_train,
						vector< LcWeakRTReg > & regressions);

void validateRegression(	vector<vector< LcValidator > > &  test2train,
							vector< vector< int > > & frm_id,
							Mat & feature_test,
							vector< LcWeakRTReg > & regressions);

Mat getScoresFromOneCluster(	vector<vector< LcValidator > > & validators,
								vector< int > & frm_id
								);
								

int main(int argc, char * const argv[])
{
	LcInterface my_setting;

	if(argc==1)
	{
		//char *const setting_string[] ={ "","-b","-v","l","-t","GOPR2384_cab","-s", "175", "-e", "500", "-r","25","-p","GOPR2384_cab","-f","./../../_FGS/","-g","5","-S","175","-E","500","-R","25"};

		char *const setting_string[] ={ "","-a","-v","l","-t","GOPR2384_cab","-s", "150", "-e", "5300", "-r","50","-p","GOPR2382_cab","-f","./../../_FGS/","-g","1","-S","175","-E","11150","-R","25","-h"};

		my_setting.input( 24 + 1 ,setting_string);
	}
	else my_setting.input( argc, argv);

	ClassifierRank my_crank;
	//Kmeans my_crank;

	LcVideoReadExt my_reader = my_setting.getTrainVideo();

	my_crank.setReader( &my_reader);

	my_crank.setFeatureCode( my_setting.feature_code_string.c_str() );

	my_crank.setClassifier( my_setting.getClassifier() );

	if(my_setting.IF_TRAIN)
	{
		//swaped name because of interface
		LcVideoReadExt test_reader   = my_setting.getTestVideo();
		LcVideoReadExt train_reader  = my_setting.getTrainVideo();

		cout << "Do Crank Cluster" << endl;
		cout << "train database : " << train_reader.video_name << endl;
		cout	<< train_reader.f_start << " : "
				<< train_reader.f_rate << " : "
				<< train_reader.f_end << endl;
		cout << "test database : " << test_reader.video_name << endl;
		cout	<< test_reader.f_start << " : "
				<< test_reader.f_rate << " : "
				<< test_reader.f_end << endl;

		LcGlobalExtractor * global_extractor
			= my_setting.my_global_extractor;

		estimateCluster(	&test_reader,
							&train_reader,
							my_setting.feature_code_string.c_str(),
							global_extractor,
							my_setting.addition_str,
							my_setting.IF_KMEAN,
							my_setting.IF_REG);
	}


	if(my_setting.IF_TEST)
	{

		LcGlobalExtractor * global_extractor
			= my_setting.my_global_extractor;

		my_crank.setGlobalExtractor( global_extractor);

		//design to do frame-wise cross test

		LcVideoReadExt test_reader   = my_setting.getTestVideo();
		LcVideoReadExt train_reader  = my_setting.getTrainVideo();


		cout << "Do cross DataBase frame-wise predicting" << endl;
		cout << "train database : " << train_reader.video_name << endl;
		cout	<< train_reader.f_start << " : "
				<< train_reader.f_rate << " : "
				<< train_reader.f_end << endl;
		cout << "test database : " << test_reader.video_name << endl;
		cout	<< test_reader.f_start << " : "
				<< test_reader.f_rate << " : "
				<< test_reader.f_end << endl;

		crossFrameTest(		&test_reader,
							&train_reader,
							my_setting.feature_code_string.c_str(),
							my_crank.my_extractor,
							my_crank.my_classifier);
		
		
	}

	return 0;
}


void estimateCluster(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						const char * feature_code_string,
						LcGlobalExtractor * global_extractor,
						string addition_str,
						bool IF_KMEAN,
						bool IF_REG)
{

	cout << "init validate score of cross frame test" << endl;

	vector<vector< LcValidator > > train2train;
	vector<vector< LcValidator > > test2train;

	initCrossValidator( test_reader, train_reader, feature_code_string,
						train2train, test2train );

	int n_train = (int) train2train.size();
	int n_test = (int) test2train.size();

	Mat feature_test;
	Mat feature_train;

	bool IF_LOAD_GLOBAL_FEAT  = false;

	getGlobalFeature(	test_reader, train_reader,
						feature_test, feature_train,
						global_extractor, !IF_LOAD_GLOBAL_FEAT);

	if(IF_KMEAN)
	{

		FILE * out;

		{
			char fname[400];

			sprintf(fname,	"./report/kmean_%s_on_%s_%s_%s.txt",
							test_reader->video_name.c_str(),
							train_reader->video_name.c_str(),
							feature_code_string,
							addition_str.c_str() );

			out = fopen( fname , "w" );
						
		}

		vector< int > cluster_lab;

		kmeanCluster( train2train, feature_train, cluster_lab);		

		vector< vector< int > > frm_id;

		clusterLab2Map( cluster_lab, frm_id );

		vector< LcWeakRTReg > regressions;

		if( IF_REG)
		{
			regressionScore( train2train, frm_id, feature_train, regressions);
		}

		if(!IF_REG)
		{
			LcValidator score_train = getClusterValidator( frm_id, cluster_lab,
				feature_train, feature_train, train2train);

			fprintf( out , "%f\t1\t%f\t%f\t%f\t%f\n" , score_train.getF1()
					, score_train.tp, score_train.fp, score_train.fn, score_train.tn );		

			LcValidator score_test =  getClusterValidator( frm_id, cluster_lab, feature_train, feature_test , test2train);

			fprintf( out , "%f\t0\t%f\t%f\t%f\t%f\n" , score_test.getF1()
				, score_test.tp, score_test.fp, score_test.fn, score_test.tn );
		}
		else
		{
			LcValidator score_train = getRegClusterValidator( frm_id, cluster_lab,
				feature_train, feature_train, train2train, regressions);

			fprintf( out , "%f\t1\t%f\t%f\t%f\t%f\n" , score_train.getF1()
					, score_train.tp, score_train.fp, score_train.fn, score_train.tn );		

			LcValidator score_test =  getRegClusterValidator( frm_id, cluster_lab,
				feature_train, feature_test , test2train, regressions);

			fprintf( out , "%f\t0\t%f\t%f\t%f\t%f\n" , score_test.getF1()
				, score_test.tp, score_test.fp, score_test.fn, score_test.tn );
		}

		fclose( out );

	}
	else
	{

		FILE * out;

		{
			char fname[400];

			sprintf(fname,	"./report/log_%s_on_%s_%s_%s.txt",
							test_reader->video_name.c_str(),
							train_reader->video_name.c_str(),
							feature_code_string,
							addition_str.c_str() );

			out = fopen( fname , "w" );
						
		}

		float val_step = 0.05f;
		if(IF_REG) val_step = 0.1f;

		float beta_step = 1.5f;
		if(IF_REG) beta_step = 2.0f;

		for(float val = 0.005f ; val < 0.95f ; val+= val_step )
			for(float beta = 0.01f; beta < 1.0f ; beta *= beta_step)
		{
			vector< int > cluster_lab;

			crankCluster( train2train, feature_train, cluster_lab, beta, val);

			vector< vector< int > > frm_id;

			clusterLab2Map( cluster_lab, frm_id );

			vector< LcWeakRTReg > regressions;

			if( IF_REG)
			{
				regressionScore( train2train, frm_id, feature_train, regressions);
			}

			LcValidator score_train;

			if(IF_REG)
				score_train = getRegClusterValidator( frm_id, cluster_lab,
					feature_train, feature_train, train2train,regressions);
			else
				score_train = getClusterValidator( frm_id, cluster_lab,
					feature_train, feature_train, train2train);
			
			fprintf( out , "%f\t%f\t%f\t1\t%f\t%f\t%f\t%f\n" , val , beta, score_train.getF1()
				, score_train.tp, score_train.fp, score_train.fn, score_train.tn );

			LcValidator score_test;

			if(IF_REG)
				score_test =  getRegClusterValidator( frm_id, cluster_lab,
					feature_train, feature_test , test2train,regressions);
			else
				score_test =  getClusterValidator( frm_id, cluster_lab,
					feature_train, feature_test , test2train);

			fprintf( out , "%f\t%f\t%f\t0\t%f\t%f\t%f\t%f\n" , val , beta, score_test.getF1()
				, score_test.tp, score_test.fp, score_test.fn, score_test.tn );
			
		}

		fclose( out );
	}

}


void validateRegression(	vector<vector< LcValidator > > &  test2train,
							vector< vector< int > > & frm_id,
							Mat & feat_test,
							vector< LcWeakRTReg > & regressions)
{
	int _n = (int ) test2train.size();

	int _n_cluster = (int) frm_id.size();
	
	Mat scores = Mat::zeros( _n , _n_cluster, 5 );		

	for(int i = 0  ; i < _n_cluster ; i++)
	{
		Mat temp = getScoresFromOneCluster( test2train, frm_id[i] );

		temp.copyTo( scores.col( i ) );		
	}

	Mat reconstruct_scores  = Mat::zeros( _n , _n_cluster, 5 );

	for(int i = 0  ; i < _n_cluster ; i++)
	{
		Mat temp; Mat empty = Mat::Mat();

		regressions[i].predict(feat_test,temp, empty);

		temp.copyTo( reconstruct_scores.col( i ) );
	}

	if(0){
		Mat dsp; cv::resize( reconstruct_scores, dsp, Size(600,800 ) );
		dsp = dsp.t();
		lc::colorshow("debug_regression", dsp);		
	}

	if(0){
		Mat dsp; cv::resize( scores, dsp, Size(600,800 ) );
		dsp = dsp.t();
		lc::colorshow("debug_scores", dsp);
		cv::waitKey(0);
		cv::destroyAllWindows();
	}
}


void regressionScore(	vector<vector< LcValidator > > & train2train, 
						vector< vector< int > > & frm_id,
						Mat & feat_train,
						vector< LcWeakRTReg > & regressions)
{
	int _n = (int ) train2train.size();

	int _n_cluster = (int) frm_id.size();
	
	Mat scores = Mat::zeros( _n , _n_cluster, 5 );

	regressions.resize( _n_cluster );	

	for(int i = 0  ; i < _n_cluster ; i++)
	{
		Mat temp = getScoresFromOneCluster( train2train, frm_id[i] );

		temp.copyTo( scores.col( i ) );

		regressions[i].train( feat_train, temp );
	}

	Mat reconstruct_scores  = Mat::zeros( _n , _n_cluster, 5 );

	for(int i = 0  ; i < _n_cluster ; i++)
	{
		Mat temp; Mat empty = Mat::Mat();

		regressions[i].predict(feat_train,temp, empty);

		temp.copyTo( reconstruct_scores.col( i ) );
	}

	if(0){
		Mat dsp; cv::resize( reconstruct_scores, dsp, Size(600,800 ) );
		dsp = dsp.t();
		lc::colorshow("debug_regression", dsp);		
	}

	if(0){
		Mat dsp; cv::resize( scores, dsp, Size(600,800 ) );
		dsp = dsp.t();
		lc::colorshow("debug_scores", dsp);
		cv::waitKey(0);
		cv::destroyAllWindows();
	}
}

Mat getScoresFromOneCluster(	vector<vector< LcValidator > > & validators,
								vector< int > & frm_id)
{
	int _rows = (int ) validators.size();

	Mat ans = Mat::zeros( _rows, 1, 5);

	float * p_data = ans.ptr<float>();

	for(int i = 0 ; i < _rows; i ++,p_data++)
	{
		LcValidator temp = predictFromOneCluster( validators, i , frm_id );

		*p_data = temp.getF1();
	}

	return ans;
}
						

LcValidator getRegClusterValidator(	vector< vector< int > > & frm_id,
									vector< int > & cluster_lab,
									Mat & retrival_feature,
									Mat & test_feature,
									vector<vector< LcValidator > > & scores,
									vector< LcWeakRTReg > & regressions
									)
{
	LcValidator sum_score(0,0,0,0);

	int test_n = test_feature.rows;	

	int cluster_n = (int) frm_id.size();


	for(int test_id = 0 ; test_id < test_n; test_id++)
	{
		vector< float> weight( cluster_n , 0);

		{
			Mat temp_feat = test_feature.row( test_id );
			for(int i = 0 ; i < cluster_n; i++)
			{
				Mat res; Mat label = Mat::Mat();
				regressions[i].predict( temp_feat, res, label);
				float err = 1.0f - res.at<float>(0,0);
				if(err>1.0f) err = 1.0f;
				else if (err<0) err = 0;

				weight[i] = ( (1 - err + 1e-3f)/ (err + 1e-3f));
			}
		}				

		vector< LcValidator > ress; ress.clear();

		int cluster_incount = 0;

		vector< int > sort_id;
		lc::argsort( weight, sort_id);

		float sum_weight = 0;
		for(int id = cluster_n-1; id>=0;id--)
		{
			int i = sort_id[id];
			if( cluster_n - id > 7 ) break;
			if( weight[i] > 1e-3f || sum_weight+weight[i] > 1e-3f)
			{
				LcValidator temp = predictFromOneCluster( scores, test_id, frm_id[i] );

				ress.push_back( temp );

				weight[ cluster_incount ] = weight[i]; cluster_incount++;

				sum_weight += weight[i];
			}
		}		

		weight.resize( cluster_incount );

		LcValidator frm_score = estimateWeightedAvgPredict( ress, weight );
		

		sum_score = sum_score + frm_score;

		if(test_id%10==0) cout << " " << sum_score.getF1();

		//if(test_id%5==0) system("pause");
	}

	cout << endl;

	return sum_score;
}

LcValidator getClusterValidator(	vector< vector< int > > & frm_id,
									vector< int > & cluster_lab,
									Mat & retrival_feature,
									Mat & test_feature,
									vector<vector< LcValidator > > & scores)
{
	LcValidator sum_score(0,0,0,0);

	int test_n = test_feature.rows;

	int knn = 25;

	int cluster_n = (int) frm_id.size();

	knn = min( knn , retrival_feature.rows );

	for(int test_id = 0 ; test_id < test_n; test_id++)
	{
		cv::flann::Index _flann(retrival_feature, cv::flann::KDTreeIndexParams(4));

		Mat inds; Mat dists;

		_flann.knnSearch(test_feature.row( test_id ) , inds, dists,knn,cv::flann::SearchParams(64));

		vector< float> weight( cluster_n , 0);

		for(int k = 0;k< knn ;k++)
		{
			float m_weight = 1.0f;

			int & id = inds.at<int>(0,k);

			if(id<0) continue;

			int cluster_id = cluster_lab[id];

			weight[cluster_id] += m_weight;
		}

		vector< LcValidator > ress; ress.clear();

		int cluster_incount = 0;

		for(int i = 0; i < cluster_n;i++)
			if( weight[i] > 1e-3f )
			{
				LcValidator temp = predictFromOneCluster( scores, test_id, frm_id[i] );

				ress.push_back( temp );

				weight[ cluster_incount ] = weight[i]; cluster_incount++;

				//cout << temp.getF1() << " ";
			}

		//cout << endl;

		weight.resize( cluster_incount );

		LcValidator frm_score = estimateWeightedAvgPredict( ress, weight );
		

		sum_score = sum_score + frm_score;

		if(test_id%10==0) cout << " " << sum_score.getF1();

		//if(test_id%5==0) system("pause");
	}

	cout << endl;

	return sum_score;

}

LcValidator predictFromOneCluster(	vector<vector< LcValidator > > & validators,
									int predict_id,
									vector< int > & frm_id )
{
	vector< LcValidator > temp;
	
	int n_frm_in_cluster = (int) frm_id.size();

	temp.resize( n_frm_in_cluster );

	for(int i = 0 ; i < n_frm_in_cluster; i++)
		temp[i] = validators[ predict_id ][ frm_id[i] ];

	return estimateAvgPredict( temp );
}

Mat getFinOrder(	vector<vector< LcValidator > >  & train2train,
					vector< int > & order )
{
	int _n = (int) order.size();

	Mat D = Mat::zeros( _n , _n , 5);

	for(int i = 0 ; i < _n ; i ++)
		for(int j = 0 ; j < _n ; j++)
			//D.at<float>( i , j) = train2train[ order[i] ][ order[j] ].getF1();
			D.at<float>( i , j) = train2train[ order[i] ][ order[j] ].getZeroOne();

	return D;
}



void crankCluster(	vector<vector< LcValidator > > & train2train,
					Mat & feature_train,
					vector< int > & cluster_lab,
					float beta, float val)
{
	Mat D_classifier = getClassifierAffine(train2train);
	Mat D_global = getGlobalFeatAffine(feature_train);	

	Mat D = beta * ( D_global*(1.0-val) + D_classifier * val );

	Mat specturm = getLaplacian( D );

	int cluster_n = 25;

	EMcluster( specturm, cluster_lab , cluster_n);
}

void kmeanCluster(	vector<vector< LcValidator > > & train2train,
					Mat & feature_train,
					vector< int > & cluster_lab)
{

	int attempts = 10;
	TermCriteria crit(TermCriteria::MAX_ITER +TermCriteria::EPS,30,0.01);
	Mat centers;
	Mat kmeans_labs;

	int cluster_n = 25;

	kmeans(feature_train,cluster_n,kmeans_labs,crit,attempts,KMEANS_PP_CENTERS);

	cluster_lab.resize( feature_train.rows );

	for(int i = 0; i< feature_train.rows;i++) cluster_lab[i] = kmeans_labs.at<int>(i);
}

void getGlobalFeature(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						Mat & feature_test,
						Mat & feature_train,
						LcGlobalExtractor * global_extractor,
						bool reload)
{

	int n_train = train_reader->getN();
	int n_test =  test_reader->getN();

	if( reload )
	{
		int dim = global_extractor->dim;

		feature_train = Mat::zeros( n_train, dim, 5);
		feature_test  = Mat::zeros( n_test , dim, 5);

		{
			train_reader->reset();
			for(int i = 0 ; i < n_train;i ++)
			{
				if(i%10==9) cout << i+1 << "/" << n_train << " ";
				Mat frm;
				if(train_reader->readNext( frm )){ cout << "early break" << endl;break;}
				Mat temp; global_extractor->work( frm, temp);
				temp.copyTo( feature_train.row(i) );
			}
			cout << endl;
		}

		{
			test_reader->reset();
			for(int i = 0 ; i < n_test;i ++)
			{
				if(i%10==9) cout << i+1 << "/" << n_test << " ";
				Mat frm;
				if(test_reader->readNext( frm )){ cout << "early break" << endl;break;}
				Mat temp; global_extractor->work( frm, temp);
				temp.copyTo( feature_test.row(i) );
			}
			cout << endl;
		}

		lc::LcMat2Bin( "global_feature_train.bin" ,feature_train);
		lc::LcMat2Bin( "global_feature_test.bin"  ,feature_test);

	}
	else
	{
		lc::LcBin2Mat( "global_feature_train.bin" ,feature_train);
		lc::LcBin2Mat( "global_feature_test.bin"  ,feature_test);
	}
}

Mat getClassifierAffine( vector<vector< LcValidator > >  & train2train)
{
	int _n = (int)train2train.size();

	Mat D = Mat::zeros( _n , _n , 5);

	for(int i = 0 ; i < _n; i++)
		for(int j = 0 ; j <_n;j++)
			D.at<float>(i,j) = train2train[i][j].getZeroOne()*.99f;//

	double th = 0.49;
	D = max(D-th,0);
	D = 2.0 - th - th - D - D.t();

	float lambda_D = lc::reduceMat( D, CV_REDUCE_AVG) + 1e-3f;

	lambda_D = 1.0f / lambda_D ;

	D = D * lambda_D;

	return D;

}

Mat getGlobalFeatAffine(Mat & feat)
{
	int _n = feat.rows;

	Mat ans = Mat::zeros( _n , _n, 5);

	for(int i= 0 ; i < _n -1; i++)
		for(int j = i+1; j< _n;j++)
			ans.at<float>(i,j) = ans.at<float>(j,i)
			= (float) cv::norm( feat.row(i) - feat.row(j), NORM_L2);

	float lambda_D = lc::reduceMat( ans, CV_REDUCE_AVG) + 1e-3f;
	lambda_D = 1.0f / lambda_D ;
	ans = ans * lambda_D;

	return ans;
}

void initCrossValidator(	LcVideoReadExt * test_reader,
							LcVideoReadExt * train_reader,
							const char * feature_code_string,
							vector<vector< LcValidator > > & train2train,
							vector<vector< LcValidator > > & test2train )
{
	int train_frame_n = train_reader->getN();
	int test_frame_n = test_reader->getN();

	cout << "train dataset " << train_frame_n << " frames inside " << endl;
	cout << "test dataset " << test_frame_n << " frames inside " << endl;

	Mat train_score_record;// = Mat::zeros(train_frame_n, train_frame_n*4, 5);
	Mat test_score_record;// =  Mat::zeros(test_frame_n , train_frame_n*4, 5);

	string test_score_record_name;
	string train_score_record_name;

	{
		test_score_record_name = test_reader->root + "classifier_output/train_per_frame/" 
			+ test_reader->video_name + "_on_" 
			+ train_reader->video_name + "_"
			+ feature_code_string + "_score_record.bin";

		train_score_record_name = test_reader->root + "classifier_output/train_per_frame/" 
			+ train_reader->video_name + "_on_" 
			+ train_reader->video_name + "_"
			+ feature_code_string + "_score_record.bin";
	}

	{
		cout << "try to load " << test_score_record_name << endl;

		lc::LcBin2Mat( test_score_record_name.c_str() ,test_score_record);	

		if( test_score_record.rows != test_frame_n || 
			test_score_record.cols != train_frame_n*4 )
		{
			cout << " size of test score matrix unmatch! " << endl;
			cout << test_score_record.rows << " by " << test_score_record.cols << endl;
			return;
		}
	}

	{
		cout << "try to load " << train_score_record_name << endl;

		lc::LcBin2Mat( train_score_record_name.c_str() ,train_score_record);	

		if( train_score_record.rows != train_frame_n || 
			train_score_record.cols != train_frame_n*4 )
		{
			cout << " size of train score matrix unmatch! " << endl;
			cout << train_score_record.rows << " by " << train_score_record.cols << endl;
			return;
		}
	}

	train2train.resize( train_frame_n );
	for(int i = 0 ; i < train_frame_n ; i++) train2train[i].resize( train_frame_n );

	test2train.resize( test_frame_n );
	for(int i = 0 ; i < test_frame_n ; i++) test2train[i].resize( train_frame_n );


	for(int i = 0 ; i < train_frame_n ; i++)
	{
		vector< LcValidator >  & _test_one_frame = train2train[i];
		for(int j = 0 ; j < train_frame_n ; j++)
		{
			_test_one_frame[j] = LcValidator(	train_score_record.at<float>(i,j),
												train_score_record.at<float>(i,j + train_frame_n),
												train_score_record.at<float>(i,j + train_frame_n*2),
												train_score_record.at<float>(i,j + train_frame_n*3));

		}
	}

	for(int i = 0 ; i < test_frame_n ; i++)
	{
		vector< LcValidator >  & _test_one_frame = test2train[i];
		for(int j = 0 ; j < train_frame_n ; j++)
		{
			_test_one_frame[j] = LcValidator(	test_score_record.at<float>(i,j),
												test_score_record.at<float>(i,j + train_frame_n),
												test_score_record.at<float>(i,j + train_frame_n*2),
												test_score_record.at<float>(i,j + train_frame_n*3));

		}
	}


}


void crossFrameTest(	LcVideoReadExt * test_reader,
						LcVideoReadExt * train_reader,
						const char * feature_code_string,
						LcFeatureExtractor * my_extractor,
						LcClassifier * my_classifier)
{
	int train_frame_n = train_reader->getN();
	int frame_n = test_reader->getN();

	Mat score_record = Mat::zeros(frame_n, train_frame_n*4, 5);

	string score_record_name;

	{
		score_record_name = test_reader->root + "classifier_output/train_per_frame/" 
			+ test_reader->video_name + "_on_" 
			+ train_reader->video_name + "_"
			+ feature_code_string + "_score_record.bin";
	}

	cout << "will write to " << score_record_name << endl;

	test_reader->reset();

	for(int test_frame_id = test_reader->f_start, score_record_i = 0;
		test_frame_id  <= test_reader->f_end;
		test_frame_id += test_reader->f_rate, score_record_i++)
	{
		Mat frm, gt;

		Mat desc, lab;

		int statu = test_reader->readNext(frm);

		test_reader->getLabel( gt );

		if(!gt.data) continue;

		my_extractor->work( frm, desc, gt , lab );

		cout << "==========test on frame " <<
			test_frame_id << " ==============" << endl;

		for(int train_frame_id = train_reader->f_start ,score_record_j = 0;
			train_frame_id <= train_reader->f_end;
			train_frame_id += train_reader->f_rate)
		{
			string classifier_name;

			{
				stringstream _num_str; _num_str << train_frame_id;
				classifier_name = train_reader->root + "classifier_output/train_per_frame/" 
					+ train_reader->video_name + "_" + feature_code_string + "_" +  _num_str.str() ;
			}

			cout << "\ttry to load " << classifier_name << endl;

			my_classifier->load( classifier_name.c_str() );

			Mat res;
			LcValidator temp_validator = my_classifier->predict( desc, res, lab );

			score_record.at<float>(score_record_i,score_record_j) = temp_validator.tp; score_record_j++;
			score_record.at<float>(score_record_i,score_record_j) = temp_validator.fp; score_record_j++;
			score_record.at<float>(score_record_i,score_record_j) = temp_validator.fn; score_record_j++;
			score_record.at<float>(score_record_i,score_record_j) = temp_validator.tn; score_record_j++;

		}
	}

	lc::LcMat2Bin( score_record_name.c_str() ,score_record);	
}


void EMcluster( Mat & specturm, std::vector<int> & lb,int cluster_n)
{
	if(1)
	{
		specturm = specturm.t();

		int n = specturm.rows;	

		lb.clear();
		lb.resize(n);	

		cluster_n = min( n/2 , cluster_n);

		cv::EM gmm_model = EM::EM( cluster_n );

		Mat lab_mat;
		Mat class_probs;

		//train GMM using EM in OpenCV library
		gmm_model.train( specturm , cv::noArray() , lab_mat,class_probs);

		// trans opencv mat into vector
		for(int i = 0;i<n;i++) lb[i] = lab_mat.at<int>(i);

		specturm = specturm.t();
	}
	else
	{
		specturm = specturm.t();

		vector<vector<LcValidator> > empty;
		kmeanCluster( empty, specturm, lb);

		specturm = specturm.t();
	}
}

Mat getLaplacian( Mat & D)
{
	int m = 6;

	int n = D.rows;

	Mat W;

	cv::exp( -D, W);

	Mat diag_D;

	cv::reduce( W , diag_D ,0,CV_REDUCE_SUM);

	W = -W;

	for(int i = 0;i<n;i++) W.at<float>(i,i) += diag_D.at<float>(i);

	for(int i = 0;i<n;i++) diag_D.at<float>(i) = 1.0f / sqrt( diag_D.at<float>(i));

	for(int i = 0;i<n;i++)
	{
		float v = diag_D.at<float>(i);
		for(int j = 0;j<n;j++) W.at<float>(i,j)*=v;
	}

	for(int i = 0;i<n;i++)
	{
		float v = diag_D.at<float>(i);
		for(int j = 0;j<n;j++) W.at<float>(j,i)*=v;
	}

	Mat eigen_value, eigen_vector;
	cv::eigen( W, eigen_value,eigen_vector);

	Mat ans;

	eigen_vector.rowRange(n-m-1,n-1).copyTo(ans);

	return ans;
}

void clusterLab2Map(	vector< int > & cluster_lab, 
						vector< vector< int > >  & frm_id)
{
	int max_lab = 0;
	int n = (int)cluster_lab.size();
	for(int i = 0 ; i < n ; i ++) if(cluster_lab[i]>max_lab) max_lab = cluster_lab[i];	

	max_lab++;
	vector< int > count(max_lab,0);

	for(int i = 0 ; i <n ; i++) count[ cluster_lab[i] ]++;

	vector< int > map( max_lab, -1);

	int n_cluster = 0;
	for(int i = 0; i < max_lab; i++)
		if(count[i]>0){ map[i] = n_cluster; n_cluster++; }

	cout << n_cluster << " clusters actually get" << endl;

	frm_id.resize( n_cluster );

	for(int i = 0; i < max_lab; i++)
		if(count[i]>0){ frm_id[map[i]].reserve( count[i] ); }

	for(int i = 0; i <n; i++)
	{
		cluster_lab[i] = map[ cluster_lab[i] ];
		frm_id[ cluster_lab[i] ].push_back( i );
	}
}